From 7d53c399c3425a601c56209385ac153391b9bfda Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=98yvind=20Kol=C3=A5s?= Date: Fri, 18 Aug 2017 23:15:43 +0200 Subject: [PATCH] add BablSpace for specifying chromaticities/TRC --- babl/Makefile.am | 2 + babl/babl-classes.h | 3 + babl/babl-internal.c | 1 + babl/babl-space.c | 324 +++++++++++++++++++++++++++++++++++++++++++ babl/babl-space.h | 206 +++++++++++++++++++++++++++ babl/babl.h | 97 ++++++++++++- 6 files changed, 631 insertions(+), 2 deletions(-) create mode 100644 babl/babl-space.c create mode 100644 babl/babl-space.h diff --git a/babl/Makefile.am b/babl/Makefile.am index 9c697a3..9d39687 100644 --- a/babl/Makefile.am +++ b/babl/Makefile.am @@ -31,6 +31,7 @@ c_sources = \ babl-ref-pixels.c \ babl-sampling.c \ babl-sanity.c \ + babl-space.c \ babl-type.c \ babl-util.c \ babl-cpuaccel.c \ @@ -59,6 +60,7 @@ h_sources = \ babl-mutex.h \ babl-ref-pixels.h \ babl-sampling.h \ + babl-space.h \ babl-type.h \ babl-types.h \ babl-util.h diff --git a/babl/babl-classes.h b/babl/babl-classes.h index fa25c4e..054e910 100644 --- a/babl/babl-classes.h +++ b/babl/babl-classes.h @@ -38,6 +38,7 @@ enum { BABL_COMPONENT, BABL_MODEL, BABL_FORMAT, + BABL_SPACE, BABL_CONVERSION, BABL_CONVERSION_LINEAR, @@ -57,6 +58,7 @@ enum { #include "babl-type.h" #include "babl-sampling.h" +#include "babl-space.h" #include "babl-component.h" #include "babl-model.h" #include "babl-format.h" @@ -75,6 +77,7 @@ typedef union _Babl BablInstance instance; BablType type; BablSampling sampling; + BablSpace space; BablComponent component; BablModel model; BablFormat format; diff --git a/babl/babl-internal.c b/babl/babl-internal.c index 64168cf..789cf6a 100644 --- a/babl/babl-internal.c +++ b/babl/babl-internal.c @@ -30,6 +30,7 @@ static const char *class_names[] = "BablComponent", "BablModel", "BablFormat", + "BablSpace", "BablConversion", "BablConversionLinear", "BablConversionPlane", diff --git a/babl/babl-space.c b/babl/babl-space.c new file mode 100644 index 0000000..c264a1c --- /dev/null +++ b/babl/babl-space.c @@ -0,0 +1,324 @@ +/* babl - dynamically extendable universal pixel conversion library. + * Copyright (C) 2017 Øyvind Kolås. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see + * . + */ + +#define MAX_SPACES 100 + +#include "config.h" +#include "babl-internal.h" +#include "base/util.h" + +static BablSpace space_db[MAX_SPACES]; + +static void babl_chromatic_adaptation_matrix (const double *whitepoint, + const double *target_whitepoint, + double *chad_matrix) +{ + double bradford[9]={ 0.8951000, 0.2664000, -0.1614000, + -0.7502000, 1.7135000, 0.0367000, + 0.0389000, -0.0685000, 1.0296000}; + double bradford_inv[9]={0.9869929,-0.1470543, 0.1599627, + 0.4323053, 0.5183603, 0.0492912, + -0.0085287, 0.0400428, 0.9684867}; + + double vec_a[3]; + double vec_b[3]; + + babl_matrix_mul_vector (bradford, whitepoint, vec_a); + babl_matrix_mul_vector (bradford, target_whitepoint, vec_b); + + memset (chad_matrix, 0, sizeof (double) * 9); + + chad_matrix[0] = vec_b[0] / vec_a[0]; + chad_matrix[4] = vec_b[1] / vec_a[1]; + chad_matrix[8] = vec_b[2] / vec_a[2]; + + babl_matrix_mul_matrix (bradford_inv, chad_matrix, chad_matrix); + babl_matrix_mul_matrix (chad_matrix, bradford, chad_matrix); +} + +static void babl_space_compute_matrices (BablSpace *space) +{ +#define _ space-> + /* transform spaces xy(Y) specified data to XYZ */ + double red_XYZ[3] = { _ xr / _ yr, 1.0, ( 1.0 - _ xr - _ yr) / _ yr}; + double green_XYZ[3] = { _ xg / _ yg, 1.0, ( 1.0 - _ xg - _ yg) / _ yg}; + double blue_XYZ[3] = { _ xb / _ yb, 1.0, ( 1.0 - _ xb - _ yb) / _ yb}; + double whitepoint_XYZ[3] = { _ xw / _ yw, 1.0, ( 1.0 - _ xw - _ yw) / _ yw}; + double D50_XYZ[3] = {0.9642, 1.0, 0.8249}; +#undef _ + + double mat[9] = {red_XYZ[0], green_XYZ[0], blue_XYZ[0], + red_XYZ[1], green_XYZ[1], blue_XYZ[1], + red_XYZ[2], green_XYZ[2], blue_XYZ[2]}; + double inv_mat[9]; + double S[3]; + double chad[9]; + + babl_matrix_invert (mat, inv_mat); + babl_matrix_mul_vector (inv_mat, whitepoint_XYZ, S); + + mat[0] *= S[0]; mat[1] *= S[1]; mat[2] *= S[2]; + mat[3] *= S[0]; mat[4] *= S[1]; mat[5] *= S[2]; + mat[6] *= S[0]; mat[7] *= S[1]; mat[8] *= S[2]; + + babl_chromatic_adaptation_matrix (whitepoint_XYZ, D50_XYZ, chad); + + babl_matrix_mul_matrix (chad, mat, mat); + + memcpy (space->RGBtoXYZ, mat, sizeof (mat)); +#if 0 + { + int i; + fprintf (stderr, "\n%s RGBtoXYZ:\n", space->name); + for (i = 0; i < 9; i++) + { + fprintf (stderr, "%f ", mat[i]); + if (i%3 == 2) + fprintf (stderr, "\n"); + } + } +#endif + + babl_matrix_invert (mat, mat); + +#if 0 + { + int i; + fprintf (stderr, "\n%s XYZtoRGB:\n", space->name); + for (i = 0; i < 9; i++) + { + fprintf (stderr, "%f ", mat[i]); + if (i%3 == 2) + fprintf (stderr, "\n"); + } + } +#endif + + memcpy (space->XYZtoRGB, mat, sizeof (mat)); +} + +void +babl_space_get_chromaticities (const Babl *space, + double *xw, double *yw, + double *xr, double *yr, + double *xg, double *yg, + double *xb, double *yb) +{ + if (!space) + return; + + if (xw) *xw = space->space.xw; + if (yw) *yw = space->space.yw; + + if (xr) *xr = space->space.xr; + if (yr) *yr = space->space.yr; + if (xg) *xg = space->space.xg; + if (yg) *yg = space->space.yg; + if (xb) *xb = space->space.xb; + if (yb) *yb = space->space.yb; +} + +BablTRC babl_space_get_trc (const Babl *space, double *gamma) +{ + if (!space) + return 0; + if (gamma) *gamma = space->space.gamma; + return space->space.trc; +} + +const Babl * +babl_space (const char *name) +{ + int i; + for (i = 0; space_db[i].instance.class_type; i++) + if (!strcmp (space_db[i].instance.name, name)) + return (Babl*)&space_db[i]; + return NULL; +} + +const Babl * +babl_space_new (const char *name, + double wx, double wy, + double rx, double ry, + double gx, double gy, + double bx, double by, + double gamma, BablTRC trc) +{ + int i=0; + static BablSpace space; + space.instance.class_type = BABL_SPACE; + space.instance.id = 0; + + space.xr = rx; + space.yr = ry; + space.xg = gx; + space.yg = gy; + space.xb = bx; + space.yb = by; + space.xw = wx; + space.yw = wy; + space.gamma = gamma; + space.trc = trc; + + for (i = 0; space_db[i].instance.class_type; i++) + { + int offset = ((char*)&space_db[i].xr) - (char*)(&space_db[i]); + int size = ((char*)&space_db[i].trc) - ((char*)&space_db[i].xr); + + if (memcmp ((char*)(&space_db[i]) + offset, ((char*)&space) + offset, size)==0) + { + return (void*)&space_db[i]; + } + } + if (i >= MAX_SPACES-1) + { + babl_log ("too many BablSpaces"); + return NULL; + } + space_db[i]=space; + space_db[i].instance.name = space_db[i].name; + if (name) + sprintf (space_db[i].name, "%s", name); + else + sprintf (space_db[i].name, "space-%.4f,%.4f_%.4f,%.4f_%.4f,%.4f_%.4f,%.4f_%.4f,%i", + wx,wy,rx,ry,bx,by,gx,gy,gamma,trc); + + /* compute matrixes */ + babl_space_compute_matrices (&space_db[i]); + + return (Babl*)&space_db[i]; +} + +void +babl_space_class_for_each (BablEachFunction each_fun, + void *user_data) +{ + int i=0; + for (i = 0; space_db[i].instance.class_type; i++) + if (each_fun (BABL (&space_db[i]), user_data)) + return; +} + +void +babl_space_class_init (void) +{ + /* we register sRGB first so that lookups for it is fastest */ +#ifdef CCE + babl_space_new ("sRGB", + 0.3127, 0.3290, /* D65 */ + 0.6400, 0.3300, + 0.3000, 0.6000, + 0.1500, 0.0600, + 1.0, BABL_TRC_LINEAR); +#else + babl_space_new ("sRGB", + 0.3127, 0.3290, /* D65 */ + //0.3127, 0.3290, /* D65 */ + 0.6400, 0.3300, + 0.3000, 0.6000, + 0.1500, 0.0600, + 2.2, BABL_TRC_SRGB); +#endif + + babl_space_new ("Adobe", + 0.3127, 0.3290, /* D65 */ + 0.6400, 0.3300, + 0.2100, 0.7100, + 0.1500, 0.0600, + 2.2, BABL_TRC_GAMMA); + + babl_space_new ("Apple", + 0.3127, 0.3290, /* D65 */ + 0.6250, 0.3400, + 0.2800, 0.5950, + 0.1550, 0.0700, + 1.8, BABL_TRC_GAMMA); + + babl_space_new ("Best", + 0.34567, 0.3585, /* D50 */ + 0.7347, 0.2653, + 0.2150, 0.7750, + 0.1300, 0.0350, + 2.2, BABL_TRC_GAMMA); + + babl_space_new ("Beta", + 0.34567, 0.3585, /* D50 */ + 0.6888, 0.3112, + 0.1986, 0.7551, + 0.1265, 0.0352, + 2.2, BABL_TRC_GAMMA); + + babl_space_new ("ProPhoto", + 0.34567, 0.3585, /* D50 */ + 0.7347, 0.2653, + 0.1596, 0.8404, + 0.0366, 0.0001, + 1.8, BABL_TRC_GAMMA); + + babl_space_new ("Bruce", + 0.3127, 0.3290, /* D65 */ + 0.6400, 0.3300, + 0.2800, 0.6500, + 0.1500, 0.0600, + 1.8, BABL_TRC_GAMMA); + + babl_space_new ("PAL", + 0.3127, 0.3290, /* D65 */ + 0.6400, 0.3300, + 0.2900, 0.6000, + 0.1500, 0.0600, + 2.2, BABL_TRC_GAMMA); + + babl_space_new ("SMPTE-C", + 0.3127, 0.3290, /* D65 */ + 0.6300, 0.3300, + 0.3100, 0.5950, + 0.1550, 0.0700, + 2.2, BABL_TRC_GAMMA); + + babl_space_new ("ColorMatch", + 0.34567, 0.3585, /* D50 */ + 0.6300, 0.3400, + 0.2950, 0.6050, + 0.1500, 0.0750, + 1.8, BABL_TRC_GAMMA); + + babl_space_new ("Don RGB 4", + 0.34567, 0.3585, /* D50 */ + 0.6960, 0.3000, + 0.2150, 0.7650, + 0.1300, 0.0350, + 1.8, BABL_TRC_GAMMA); + + babl_space_new ("WideGamutRGB", + 0.34567, 0.3585, /* D50 */ + 0.7350, 0.2650, + 0.1150, 0.8260, + 0.1570, 0.0180, + 2.2, BABL_TRC_GAMMA); +} + +void babl_space_to_xyz (const Babl *space, const double *rgb, double *xyz) +{ + _babl_space_to_xyz (space, rgb, xyz); +} + +void babl_space_from_xyz (const Babl *space, const double *xyz, double *rgb) +{ + _babl_space_from_xyz (space, xyz, rgb); +} diff --git a/babl/babl-space.h b/babl/babl-space.h new file mode 100644 index 0000000..f13f319 --- /dev/null +++ b/babl/babl-space.h @@ -0,0 +1,206 @@ +/* babl - dynamically extendable universal pixel conversion library. + * Copyright (C) 2017, Øyvind Kolås and others. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see + * . + */ + +#ifndef _BABL_SPACE_H +#define _BABL_SPACE_H + +#include +#include +#include "base/util.h" + +BABL_CLASS_DECLARE (space); + +typedef struct +{ + BablInstance instance; + double xw; // white-point chromaticity + double yw; + + double xr; // red primary chromaticity + double yr; + + double xg; // green primary chromaticity + double yg; + + double xb; // blue primary chromaticity + double yb; + + double gamma;// gamma TRC + BablTRC trc; // flag for treatment of gamma (sRGB(1) vs just pow(0)) + char name[128]; + + double RGBtoXYZ[9]; /* matrices for conversions */ + double XYZtoRGB[9]; + + /* the space should contain matrix to/from XYZ */ + /* and before converting a span, all that needs to be + rigged is merging matrices */ + +} BablSpace; +#include +static inline double babl_space_from_linear (const Babl *space_, double value) +{ + BablSpace *space = (void*)space_; + switch (space->trc) + { + case BABL_TRC_LINEAR: + return value; + case BABL_TRC_GAMMA: + return pow (value, 1.0/space->gamma); + case BABL_TRC_SRGB: + return babl_linear_to_gamma_2_2 (value); + } + return value; +} + +static inline double babl_space_to_linear (const Babl *space_, double value) +{ + BablSpace *space = (void*)space_; + switch (space->trc) + { + case BABL_TRC_LINEAR: + return value; + case BABL_TRC_GAMMA: + return pow (value, space->gamma); + case BABL_TRC_SRGB: + return babl_gamma_2_2_to_linear (value); + } + return value; +} + +static inline float babl_space_from_linearf (const Babl *space_, float value) +{ + BablSpace *space = (void*)space_; + switch (space->trc) + { + case BABL_TRC_LINEAR: return value; + case BABL_TRC_GAMMA: return powf (value, 1.0f/space->gamma); + case BABL_TRC_SRGB: return babl_linear_to_gamma_2_2f (value); + } + return value; +} + +static inline float babl_space_to_linearf (const Babl *space_, float value) +{ + BablSpace *space = (void*)space_; + switch (space->trc) + { + case BABL_TRC_LINEAR: return value; + case BABL_TRC_GAMMA: return powf (value, space->gamma); + case BABL_TRC_SRGB: return babl_gamma_2_2_to_linearf (value); + } + return value; +} + +#define m(matr, j, i) matr[j*3+i] + +static inline void babl_matrix_mul_matrix (const double *matA_, + const double *matB_, + double *out) +{ + int i, j; + double matA[9]; + double matB[9]; + double t1, t2, t3; + memcpy (matA, matA_, sizeof (matA)); + memcpy (matB, matB_, sizeof (matB)); + + for (i = 0; i < 3; i++) + { + t1 = m(matA, i, 0); + t2 = m(matA, i, 1); + t3 = m(matA, i, 2); + + for (j = 0; j < 3; j ++) + { + m(out,i,j) = t1 * m(matB, 0, j); + m(out,i,j) += t2 * m(matB, 1, j); + m(out,i,j) += t3 * m(matB, 2, j); + } + } +} + +static inline void babl_matrix_invert (const double *in, double *out) +{ + double mat[9]; + double det, invdet; + memcpy (mat, in, sizeof (mat)); + det = m(mat, 0, 0) * (m(mat, 1, 1) *m(mat, 2, 2) - m(mat, 2, 1)*m(mat, 1, 2)) - + m(mat, 0, 1) * (m(mat, 1, 0) *m(mat, 2, 2) - m(mat, 1, 2)*m(mat, 2, 0)) + + m(mat, 0, 2) * (m(mat, 1, 0) *m(mat, 2, 1) - m(mat, 1, 1)*m(mat, 2, 0)); + invdet = 1.0 / det; + m(out, 0, 0) = (m(mat, 1, 1) * m(mat, 2, 2) - m(mat, 2, 1) * m(mat, 1, 2)) * invdet; + m(out, 0, 1) = (m(mat, 0, 2) * m(mat, 2, 1) - m(mat, 0, 1) * m(mat, 2, 2)) * invdet; + m(out, 0, 2) = (m(mat, 0, 1) * m(mat, 1, 2) - m(mat, 0, 2) * m(mat, 1, 1)) * invdet; + m(out, 1, 0) = (m(mat, 1, 2) * m(mat, 2, 0) - m(mat, 1, 0) * m(mat, 2, 2)) * invdet; + m(out, 1, 1) = (m(mat, 0, 0) * m(mat, 2, 2) - m(mat, 0, 2) * m(mat, 2, 0)) * invdet; + m(out, 1, 2) = (m(mat, 1, 0) * m(mat, 0, 2) - m(mat, 0, 0) * m(mat, 1, 2)) * invdet; + m(out, 2, 0) = (m(mat, 1, 0) * m(mat, 2, 1) - m(mat, 2, 0) * m(mat, 1, 1)) * invdet; + m(out, 2, 1) = (m(mat, 2, 0) * m(mat, 0, 1) - m(mat, 0, 0) * m(mat, 2, 1)) * invdet; + m(out, 2, 2) = (m(mat, 0, 0) * m(mat, 1, 1) - m(mat, 1, 0) * m(mat, 0, 1)) * invdet; +} + + +static inline void babl_matrix_mul_vector (const double *mat, const double *v_in, double *v_out) +{ + double val[3]={v_in[0], v_in[1], v_in[2]}; + + v_out[0] = m(mat, 0, 0) * val[0] + m(mat, 0, 1) * val[1] + m(mat, 0, 2) * val[2]; + v_out[1] = m(mat, 1, 0) * val[0] + m(mat, 1, 1) * val[1] + m(mat, 1, 2) * val[2]; + v_out[2] = m(mat, 2, 0) * val[0] + m(mat, 2, 1) * val[1] + m(mat, 2, 2) * val[2]; +#undef m +} + +static inline void babl_space_to_xyzf (const Babl *space, const float *rgb, float *xyz) +{ + BablSpace *space_ = (void*)space; + double rgbmat[3] = {rgb[0], rgb[1], rgb[2]}; + double xyzmat[3]; + babl_matrix_mul_vector (space_->RGBtoXYZ, rgbmat, xyzmat); + xyz[0] = xyzmat[0]; + xyz[1] = xyzmat[1]; + xyz[2] = xyzmat[2]; +} + +static inline void babl_space_from_xyzf (const Babl *space, const float *xyz, float *rgb) +{ + BablSpace *space_ = (void*)space; + double xyzmat[3] = {xyz[0], xyz[1], xyz[2]}; + double rgbmat[3]; + babl_matrix_mul_vector (space_->XYZtoRGB, xyzmat, rgbmat); + rgb[0] = rgbmat[0]; + rgb[1] = rgbmat[1]; + rgb[2] = rgbmat[2]; +} + +static inline void _babl_space_to_xyz (const Babl *space, const double *rgb, double *xyz) +{ + BablSpace *space_ = (void*)space; + babl_matrix_mul_vector (space_->RGBtoXYZ, rgb, xyz); +} + +static inline void _babl_space_from_xyz (const Babl *space, const double *xyz, double *rgb) +{ + BablSpace *space_ = (void*)space; + babl_matrix_mul_vector (space_->XYZtoRGB, xyz, rgb); +} + +void +babl_space_class_init (void); + +#endif diff --git a/babl/babl.h b/babl/babl.h index 546fad6..b1b1537 100644 --- a/babl/babl.h +++ b/babl/babl.h @@ -63,6 +63,7 @@ const Babl * babl_type (const char *name); const Babl * babl_sampling (int horizontal, int vertical); + /** * babl_component: * @@ -85,7 +86,86 @@ const Babl * babl_model (const char *name); * Returns the babl object representing the color format given by * @name such as for example "RGB u8", "CMYK float" or "CIE Lab u16". */ -const Babl * babl_format (const char *name); +const Babl * babl_format (const char *name); + + +/** + * babl_format_with_space: + * + * Returns the babl object representing the color format given by + * @name such as for example "RGB u8", "R'G'B'A float", "Y float" with + * a specific RGB working space used as the space, the resulting format + * has -space suffixed to it, unless the space requested is sRGB then + * the unsuffixed version is used. If a format is passed in as space + * the space of the format is used. + */ +const Babl * babl_format_with_space (const char *name, const Babl *space); + +typedef enum { + BABL_TRC_LINEAR, + BABL_TRC_GAMMA, + BABL_TRC_SRGB +} BablTRC; + +/** + * babl_space: + * + * Returns the babl object representing the specific RGB matrix color + * working space referred to by name. Babl knows of: + * sRGB, Adobe, Apple and ProPhoto + */ +const Babl * babl_space (const char *name); + +/** + * babl_space_new: + * + * Creates a new RGB matrix color space definition with the specified + * white point wx, wy, primary chromaticities rx,ry,gx,gy,bx,by and + * TRC to be used. After registering a new babl-space it can be used + * with babl_space(). + */ +const Babl * babl_space_new (const char *name, + double wx, double wy, + double rx, double ry, + double gx, double gy, + double bx, double by, + double gamma, BablTRC trc); + +void babl_space_to_xyz (const Babl *space, const double *rgb, double *xyz); +void babl_space_from_xyz (const Babl *space, const double *xyz, double *rgb); + +/* + * babl_format_get_space: + * + * Retrieve the RGB color space used for a pixel format. + */ +const Babl * babl_format_get_space (const Babl *format); + +/* + * babl_space_get_trc: + * + * Returns the type of transfer response curve used for ' annotated components + * for this space. BABL_TRC_LINEAR means no mapping, BABL_TRC_GAMMA means use + * the gamma double value and BABL_TRC_SRGB means use the sRGB gamma + * function. + * + * If a pointer to return double is not provided but is NULL, then the trc type + * is still returned. + */ +BablTRC babl_space_get_trc (const Babl *space, double *gamma); + +/** + * babl_space_get_chromaticities: + * + * Returns the CIE xyY chromaticity values for white point and primaries for a + * space. + */ +void babl_space_get_chromaticities (const Babl *space, + double *wx, double *wy, + double *rx, double *ry, + double *gx, double *gy, + double *bx, double *by); + /** * babl_fish: @@ -112,7 +192,7 @@ long babl_process (const Babl *babl_fish, /** * babl_get_name: * - * Returns a string decsribing a Babl object. + * Returns a string describing a Babl object. */ const char * babl_get_name (const Babl *babl); @@ -251,6 +331,19 @@ int babl_format_is_format_n (const Babl *format); const Babl * babl_conversion_new (const void *first_arg, ...) BABL_ARG_NULL_TERMINATED; +/** + * babl_conversion_get_source_space: + * + * Returns the RGB space defined for the source of conversion. + */ +const Babl *babl_conversion_get_source_space (const Babl *conversion); + +/** + * babl_conversion_get_destination_space: + * + * Returns the RGB space defined for the destination of conversion. + */ +const Babl *babl_conversion_get_destination_space (const Babl *conversion); /** * babl_new_palette: -- 2.30.2